package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.feign.article.IArticleClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;


@Transactional
@Service
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {

    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Autowired
    private GreenTextScan greenTextScan;

    @Autowired
    private GreenImageScan greenImageScan;

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private IArticleClient articleClient;

    @Autowired
    private WmUserMapper wmUserMapper;

    @Autowired
    private WmChannelMapper wmChannelMapper;

    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;

    @Autowired
    private Tess4jClient tess4jClient;

    /**
     * 文章的自动审核
     * @param newsId 自媒体文章id
     */
    @Async
    @Override
    public void autoScanWmNews(Integer newsId) {
        if (newsId == null) {
            return;
        }
        // 1. 查询自媒体文章
        WmNews wmNews = wmNewsMapper.selectById(newsId);
        if (wmNews == null) {
            throw new RuntimeException("文章不存在");
        }
        // 判断文章是否为待审核状态
        if (wmNews.getStatus().equals((short) 1)) {
            // 2. 文章文本审核
            // 根据文章数据，抽取出，需要审核的文本和图片
            Map<String, Object> map = extractContentAndImageList(wmNews);

            // 自定义敏感词审核
            boolean sensitiveFlag = scanTextForSensitive((String) map.get("content"), wmNews);
            if (!sensitiveFlag) {
                return;
            }

            // 阿里云内容审核
            boolean textFlag = scanTextHandle((String) map.get("content"), wmNews);
            if (!textFlag) {
                return;
            }

            // 3. 文章图片审核
            boolean imageFlag = scanImageHandle((List) map.get("imageList"), wmNews);
            if (!imageFlag) {
                return;
            }

            // 4. 审核通过，保存app端相关文章
            ResponseResult responseResult = saveAppArticle(wmNews);

            // 5. 修改状态，并保存article_id
            if (!responseResult.getCode().equals(200)) {
                throw new RuntimeException("保存文章数据失败");
            }

            wmNews.setArticleId((Long) responseResult.getData());
            updateStatus(wmNews, (short) 9, "文章发布成功");

        }

    }

    /**
     * 自定义敏感吃审核
     * @param content
     * @param wmNews
     * @return
     */
    private boolean scanTextForSensitive(String content, WmNews wmNews) {
        boolean flag = true;
        // 获取需要审核的数据
        String scanText = content + "-" + wmNews.getTitle();

        // 查询所有的敏感词
        List<String> sensitiveList = wmSensitiveMapper.selectList(null).stream()
                .map(WmSensitive::getSensitives)
                .collect(Collectors.toList());

        // 生成关键词字典
        SensitiveWordUtil.initMap(sensitiveList);

        // 获取出现的次数
        Map<String, Integer> map = SensitiveWordUtil.matchWords(scanText);

        // 判断是否命中
        if (map.size() > 0) {
            // 表示文本中有敏感词
            flag = false;
            // 修改状态
            updateStatus(wmNews, (short) 2, "文章内容有自定义的敏感吃");
        }

        return flag;

    }

    /**
     * 审核通过，保存app端相关数据
     * @param wmNews
     */
    private ResponseResult saveAppArticle(WmNews wmNews) {
        // 保存app端文章数据
        ArticleDto dto = new ArticleDto();
        BeanUtils.copyProperties(wmNews, dto);
        // 作者id和作者名称
        dto.setAuthorId(wmNews.getUserId().longValue());
        // 根据作者id查询作者名称
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if (wmUser != null && wmUser.getName() != null) {
            dto.setAuthorName(wmUser.getName());
        }

        // 频道名称
        // 根据频道id，查询频道名称
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if (wmChannel != null && wmChannel.getName() != null) {
            dto.setChannelName(wmChannel.getName());
        }

        // 文章布局
        dto.setLayout(wmNews.getType());

        dto.setCreatedTime(new Date());

        ResponseResult result = articleClient.saveArticle(dto);

        return result;
    }

    /**
     * 图片审核数据
     * @param imageList
     * @param wmNews
     */
    private boolean scanImageHandle(List<String> imageList, WmNews wmNews) {

        boolean flag = true;

        // 校验参数
        if (imageList == null || imageList.size() == 0) {
            return flag;
        }

        try {
            // 下载图片
            // 图片去重
            imageList = imageList.stream().distinct().collect(Collectors.toList());

            List<byte[]> list = imageList.stream().map(imageUrl ->
                    // 根据url下载数据
                    fileStorageService.downLoadFile(imageUrl)
            ).collect(Collectors.toList());


            // 图片内容审核
            for (byte[] bytes : list) {

                InputStream input = new ByteArrayInputStream(bytes);

                BufferedImage image = ImageIO.read(input);
                // 识别图片中的文字
                String imageText = tess4jClient.doOCR(image);

                // 审核自定义的敏感词过滤
                flag = this.scanTextForSensitive(imageText, wmNews);
                if (!flag) {
                    return false;
                }
            }


            // 图片审核
            Map map = greenImageScan.imageScan(list);

            // 判断是否审核成功，失败需要修改状态
            if (map.get("suggestion").equals("review")) {
                // 人工审核
                updateStatus(wmNews, (short) 3, "图片不确定");
                flag = false;
            } else if (map.get("suggestion").equals("block")) {
                // 审核失败
                updateStatus(wmNews, (short) 2, "图片不合规");
                flag = false;
            }

            // 状态判断
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }

        return flag;
    }

    /**
     * 审核图片数据
     * @param content
     * @param wmNews
     * @return true表示成功， false，表示失败或者人工
     */
    private boolean scanTextHandle(String content, WmNews wmNews) {

        boolean flag = true;

        try {
            // 需要审核的数据
            String scanData = content + "-" + wmNews.getTitle();

            // 审核文本数据
            Map map = greenTextScan.greeTextScan(scanData);

            // 判断是否审核成功，失败需要修改状态
            if (map.get("suggestion").equals("review")) {
                // 人工审核
                updateStatus(wmNews, (short) 3, "文本数据有歧义，转人工");
                flag = false;
            } else if (map.get("suggestion").equals("block")) {
                // 审核失败
                updateStatus(wmNews, (short) 2, "文章内容中有敏感词汇");
                flag = false;
            }

        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }

        return flag;
    }

    /**
     * 修改文章内容的状态
     * @param wmNews
     * @param status
     * @param message
     */
    private void updateStatus(WmNews wmNews, short status, String message) {
        wmNews.setStatus(status);
        wmNews.setReason(message);
        wmNewsMapper.updateById(wmNews);
    }

    /**
     * 抽取的第一个方法，获取文章数据中的文本内容和图片数据
     * @param wmNews
     */
    private Map<String, Object> extractContentAndImageList(WmNews wmNews) {

        // 将文本内容添加到字符串中
        StringBuilder sb = new StringBuilder();

        // 将图片封装到集合中
        List<String> imageList = new ArrayList<>();

        String content = wmNews.getContent();
        // 将json格式的内容转换成map集合
        List<Map> list = JSON.parseArray(content, Map.class);

        // 遍历集合
        for (Map map : list) {
            // 判断数据是否为文本
            if (map.get("type").equals("text")) {
                sb.append(map.get("value"));
            }

            // 判断数据是否为图片
            if (map.get("type").equals("image")) {
                imageList.add((String) map.get("value"));
            }
        }

        // 需要将封面图片也分装到需要审核的图片集合中
        String images = wmNews.getImages();
        String[] split = images.split(",");
        imageList.addAll(Arrays.asList(split));

        Map<String, Object> result = new HashMap<>();
        result.put("content", sb.toString());
        result.put("imageList", imageList);

        return result;
    }
}
